package cn.com.bluemoon.daps.system.controller;

import cn.com.bluemoon.daps.common.datascope.BmPermission;
import cn.com.bluemoon.daps.common.datascope.log.OperateLog;
import cn.com.bluemoon.daps.common.datascope.log.enums.ModuleObject;
import cn.com.bluemoon.daps.common.datascope.log.enums.OperateType;
import cn.com.bluemoon.daps.common.domain.BaseModel;
import cn.com.bluemoon.daps.common.domain.ResultBean;
import cn.com.bluemoon.daps.common.enums.BmStatus;
import cn.com.bluemoon.daps.common.enums.DatabaseType;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.system.dto.DatasourceDto;
import cn.com.bluemoon.daps.system.entity.DapSystemDatasource;
import cn.com.bluemoon.daps.system.entity.DapSystemDatasourceEnvironment;
import cn.com.bluemoon.daps.system.service.DapSystemDatasourceEnvironmentService;
import cn.com.bluemoon.daps.system.service.DapSystemDatasourceService;
import cn.com.bluemoon.daps.system.utils.ConnectUtils;
import cn.com.bluemoon.daps.system.utils.ImportCertificate;
import cn.com.bluemoon.daps.system.vo.SysDatasourceVo;
import cn.com.bluemoon.dataserver.apitable.api.RemoteApiTableService;
import cn.com.bluemoon.dataserver.common.dto.ApiTableResultBean;
import cn.com.bluemoon.dataserver.common.dto.QueryResult;
import cn.com.bluemoon.dataserver.common.vo.DatasourceQueryVo;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.common.collect.ImmutableMap;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@RestController
@Slf4j
@RequestMapping("/dapSystem")
@Api(tags = {"数据源管理"})
public class DapDatasourceController {

    @Resource
    DapSystemDatasourceService dapSystemDatasourceService;

    @Value("${dap.bigdata.cdh.phoenix.version:phoenix-4.13-cdh5.11}")
    private String phoenixVersion;

    @Resource
    private RemoteApiTableService remoteApiTableService;

    @Resource
    DapSystemDatasourceEnvironmentService dapSystemDatasourceEnvironmentService;

    // v1.3 数据源分页列表 ok
    @BmPermission(value = "system:database:list", name = "分页列表查询")
    @GetMapping("/datasource/page")
    @ApiOperation(value = "获取数据源分页列表，搜索支持：数据源名称、系统名称、ip、库名、账号名")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = DapSystemDatasource.class)})
    public ResultBean<IPage<SysDatasourceVo>> dataSourcePage(@ApiParam(value = "每页显示的数量", example = "0") @RequestParam Integer pageSize,
                                                                @ApiParam(value = "当前页码数", example = "0") @RequestParam Integer pageNum,
                                                                @ApiParam("数据源名称、系统名称、ip、库名、账号名") @RequestParam(required = false) String sourceName) {
        IPage<SysDatasourceVo> datasourceList = dapSystemDatasourceService.datasourcePage(pageSize, pageNum, sourceName);
        return ResultBean.ok(datasourceList);
    }

    // v1.3 数据源分页列表 ok
    @BmPermission(value = "system:database:list", name = "列表查询")
    @GetMapping("datasource/pageList")
    @ApiOperation(value = "获取数据源分页列表，带模糊搜索 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = DapSystemDatasource.class)})
    public ResultBean<IPage<DapSystemDatasource>> selectDatasourcePage(@ApiParam(value = "每页显示的数量", example = "0") @RequestParam Integer pageSize,
                                                                       @ApiParam(value = "当前页码数", example = "0") @RequestParam Integer pageNum,
                                                                       @ApiParam("数据源名称") @RequestParam(required = false) String sourceName){

        IPage<DapSystemDatasource> datasourceList = dapSystemDatasourceService.getDatasourcePageList(pageSize, pageNum, sourceName);
        return ResultBean.ok(datasourceList);
    }

    // v1.3 查看数据源详情 ok
    @BmPermission(value = "system:database:info", name = "详情")
    @GetMapping("datasource/info")
    @ApiOperation(value = "查看数据源详情 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = DatasourceDto.class)})
    public ResultBean<DatasourceDto> getDatasource (@RequestParam @ApiParam("数据源id") String id){

        DatasourceDto datasourceInfo = dapSystemDatasourceService.getDatasourceInfo(id);
        // 过滤掉impala
        List<DapSystemDatasourceEnvironment> collect = datasourceInfo.getList().stream()
                .filter(d -> null == d.getExecutionEngine()).peek(s -> s.setPassword(""))
                .collect(Collectors.toList());
        datasourceInfo.setList(collect);
        return ResultBean.ok(datasourceInfo);
    }

    // v1.3 新增或修改数据源 ok
    @BmPermission(value = "system:database:add", name = "新增")
    @PostMapping("datasource/insert")
    @ApiOperation(value = "新增或修改数据源 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = String.class)})
    @OperateLog(type = OperateType.UPDATE, moduleName = ModuleObject.SYSTEM)
    public ResultBean<String> insertDatasource (@RequestBody @Valid DapSystemDatasource dapSystemDatasource) {

        ResultBean<Boolean> result = dapSystemDatasourceService.insertDatasource(dapSystemDatasource);
        return result.getCode() == 200 ? ResultBean.ok(dapSystemDatasource.getId()) : ResultBean.error(result.getMsg());
    }

    // v1.3 删除数据源 ok
    @BmPermission(value = "system:database:delete", name = "删除")
    @DeleteMapping("datasource/delete")
    @ApiOperation(value = "删除数据源(可批量) 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = Boolean.class)})
    @OperateLog(type = OperateType.DELETE, moduleName = ModuleObject.SYSTEM)
    public ResultBean<Boolean> deleteDatasource (@ApiParam("数据源id") @RequestBody @Valid List<String> ids){

        ResultBean<Boolean> booleanResultBean = dapSystemDatasourceService.deleteBatchSource(ids);
        return booleanResultBean;
    }

    // v1.3 查看数据源环境信息 ok
    @BmPermission(value = "system:database:info", name = "详情")
    @GetMapping("datasource/environment/info")
    @ApiOperation(value = "查看数据源环境信息(包括独立元库) 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = DapSystemDatasourceEnvironment.class)})
    public ResultBean<List<DapSystemDatasourceEnvironment>> getEnvironmentInfo(@RequestParam("datasourceId") @ApiParam("数据源id") String datasourceId,
                                                                               @RequestParam("environmentType") @ApiParam("数据源环境类型，1 开发，2 测试， 3 预生产，4 生产") Integer environmentType,
                                                                               @RequestParam(value = "datasourceRole", required = false) @ApiParam(value = "数据库角色，1主库，2从库") Integer datasourceRole){
        // 查询数据库类型
        List<DapSystemDatasourceEnvironment> dapSystemDatasourceEnvironments = dapSystemDatasourceEnvironmentService
                .getBaseMapper().selectList(new LambdaQueryWrapper<DapSystemDatasourceEnvironment>()
                .eq(DapSystemDatasourceEnvironment::getEnvironment, environmentType)
                .eq(DapSystemDatasourceEnvironment::getDatasourceId, datasourceId)
                .eq(datasourceRole != null, DapSystemDatasourceEnvironment::getDatasourceRole, datasourceRole)
                .eq(DapSystemDatasourceEnvironment::getBmStatus, BmStatus.ON.getCode())
                .orderByAsc(DapSystemDatasourceEnvironment::getExecutionEngine)
                .orderByAsc(DapSystemDatasourceEnvironment::getIsInfoResource));
        dapSystemDatasourceEnvironments.forEach(e -> e.setPassword(""));
        return ResultBean.ok(dapSystemDatasourceEnvironments);
    }

    // v1.3 查看独立元库信息 ok
    @BmPermission(value = "system:database:info", name = "详情")
    @GetMapping("datasource/dependent/info")
    @ApiOperation(value = "查看独立元库信息 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = DapSystemDatasourceEnvironment.class)})
    public ResultBean<DapSystemDatasourceEnvironment> getDependentSourceInfo(@RequestParam @ApiParam("数据源id") String datasourceId,
                                                                             @RequestParam @ApiParam("数据源环境类型，1 开发，2 测试， 3 预生产，4 生产") Integer environmentType){

        // 查询数据源信息
        DapSystemDatasource dapSystemDatasource = dapSystemDatasourceService.getBaseMapper().selectById(datasourceId);
        if(dapSystemDatasource != null){
            if(dapSystemDatasource.getDatabaseType().equals(DatabaseType.HIVE)){
                DapSystemDatasourceEnvironment environment = dapSystemDatasourceEnvironmentService.getBaseMapper()
                        .selectOne(new LambdaQueryWrapper<DapSystemDatasourceEnvironment>()
                                .eq(DapSystemDatasourceEnvironment::getDatasourceId, datasourceId)
                                .eq(DapSystemDatasourceEnvironment::getEnvironment, environmentType)
                                .eq(DapSystemDatasourceEnvironment::getBmStatus, BmStatus.ON.getCode()));
                environment.setPassword("");
                return ResultBean.ok(environment);
            }else {
                return ResultBean.error(dapSystemDatasource.getDatabaseType()+"不存在独立元库");
            }
        }
        return null;
    }

    // v1.3 新增或修改数据源环境 ok
    @BmPermission(value = "system:database:add", name = "新增")
    @PostMapping("datasource/environment/update")
    @ApiOperation(value = "新增或修改数据源环境 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = String.class)})
    @OperateLog(type = OperateType.UPDATE, moduleName = ModuleObject.SYSTEM)
    public ResultBean<String> updateOrAddEnvironment(@RequestBody @Valid DapSystemDatasourceEnvironment environment) {

        DapSystemDatasource dapSystemDatasource = dapSystemDatasourceService.getBaseMapper().selectById(environment.getDatasourceId());
        if(null != dapSystemDatasource && dapSystemDatasource.getBmStatus().equals(BmStatus.ON)){
            if(!dapSystemDatasource.getDatabaseType().equals(DatabaseType.HIVE) && environment.getIsInfoResource()){
                return ResultBean.error("非hive的数据源不能创建独立元库");
            }
            if(!dapSystemDatasource.getDatabaseType().equals(DatabaseType.HIVE) && environment.getExecutionEngine() != null){
                return ResultBean.error("非hive的数据源不能创建impala引擎");
            }
            if(!dapSystemDatasource.getDatabaseType().equals(DatabaseType.MYSQL) && environment.getDatasourceRole() != null){
                return ResultBean.error("非MySQL数据源不存在主从结构");
            }
            environment.setDbType(environment.getIsInfoResource() ? DatabaseType.MYSQL : dapSystemDatasource.getDatabaseType());
            ResultBean<String> resultBean = dapSystemDatasourceEnvironmentService.updateOrInsertEnvironment(environment, dapSystemDatasource);
            return resultBean.getCode() == 200 ? ResultBean.ok(resultBean.getContent()) : ResultBean.error(resultBean.getMsg());
        }
        return ResultBean.error("绑定的数据源id不存在");
    }

    // v1.3 上传证书 ok
    @BmPermission(value = "system:database:list", name = "上传证书")
    @PostMapping("upload/certificate")
    @ApiOperation(value = "上传ssh证书 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = String.class)})
    public ResultBean<String> uploadCertificate(@RequestParam MultipartFile file){

        String string = ImportCertificate.upload(file);
        return ResultBean.ok(string);
    }

    // v1.3 下载证书 ok
    @BmPermission(value = "system:database:info", name = "下载证书")
    @PostMapping("download/certificate")
    @ApiOperation(value = "下载ssh证书 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = String.class)})
    public ResultBean<String> downloadCertificate(@RequestParam("certificateName") @ApiParam("证书名") String certificateName,
                                                  HttpServletResponse response){

        try {
            //读取文件
            InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("certificate/"+certificateName);
            InputStream fis = new BufferedInputStream(resourceAsStream);
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            // 清空response
            response.reset();
            // 设置response的Header
            response.setHeader("Content-Disposition", "attachment;filename="+java.net.URLEncoder.encode(certificateName, "utf-8"));
            response.addHeader("Content-Length", "" + buffer.length);
            OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            toClient.write(buffer);
            toClient.flush();
            toClient.close();
        } catch (IOException e) {
            log.info(e.getMessage());
        }
        return null;
    }

    // v1.3 删除数据源环境 ok
    @BmPermission(value = "system:database:delete", name = "删除数据源环境")
    @DeleteMapping("environment/delete")
    @ApiOperation(value = "删除数据源环境 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = Boolean.class)})
    @OperateLog(type = OperateType.DELETE, moduleName = ModuleObject.SYSTEM)
    public ResultBean<Boolean> deleteEnvironment (@ApiParam("数据源环境id") @RequestParam String environmentId,
                                                  @ApiParam(value = "数据源角色") @RequestParam(required = false) Integer datasourceRole){

        // 获得数据源id
        DapSystemDatasourceEnvironment dapSystemDatasourceEnvironment = dapSystemDatasourceEnvironmentService.getBaseMapper().selectById(environmentId);
        // 删除数据源环境如果存在独立元库，一并删除
        boolean update = dapSystemDatasourceEnvironmentService.update(new LambdaUpdateWrapper<DapSystemDatasourceEnvironment>()
                .eq(DapSystemDatasourceEnvironment::getDatasourceId, dapSystemDatasourceEnvironment.getDatasourceId())
                .eq(DapSystemDatasourceEnvironment::getEnvironment, dapSystemDatasourceEnvironment.getEnvironment())
                .eq(datasourceRole != null, DapSystemDatasourceEnvironment::getDatasourceRole, datasourceRole)
                .set(DapSystemDatasourceEnvironment::getBmStatus, BmStatus.DELETE.getCode()));
        return ResultBean.ok(update);
    }

    /**
     * v1.3.1 ok
     * 测试连接
     */
    @BmPermission(value = "system:database:info", name = "测试连接")
    @PostMapping("database/connect")
    @ApiOperation(value = "测试连接 版本v1.3")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = String.class)})
    public ResultBean<String> connectDatabaseTest(@RequestBody @Valid DapSystemDatasourceEnvironment environment) {
        if (null == environment.getIsInfoResource()) {
            return ResultBean.error("是否为独立源库字段不允许为空");
        }
        DapSystemDatasource dapSystemDatasource = dapSystemDatasourceService.getBaseMapper().selectById(environment.getDatasourceId());
        BmAssetUtils.isTrue(dapSystemDatasource != null && dapSystemDatasource.getDatabaseType() != null, "请先设置数据源数据库类型");
        // md5解密
        ResultBean<String> stringResultBean = ConnectUtils.decoderBase(environment.getPassword());
        if (stringResultBean.getCode() == 500) {
            return stringResultBean;
        }
        // 查看或编辑的情况
        if (!StringUtils.isEmpty(environment.getId()) && StringUtils.isEmpty(stringResultBean.getContent())) {
            DapSystemDatasourceEnvironment dapSystemDatasourceEnvironment = dapSystemDatasourceEnvironmentService.getBaseMapper().selectById(environment.getId());
            environment.setDbType(getDbType(dapSystemDatasourceEnvironment, dapSystemDatasource));
            environment.setPassword(dapSystemDatasourceEnvironment.decryptPassword());
        } else {
            // 编辑密码或新增
            environment.setDbType(getDbType(environment, dapSystemDatasource));
            environment.setPassword(stringResultBean.getContent());
        }
        return ResultBean.ok(tryGetDbVersion(environment, environment.getDbType()));
    }

    private DatabaseType getDbType(DapSystemDatasourceEnvironment environment, DapSystemDatasource dapSystemDatasource) {
        if (environment.getIsInfoResource()) return DatabaseType.MYSQL;
        if ((DatabaseType.HIVE.equals(dapSystemDatasource.getDatabaseType()) || DatabaseType.HIVE.equals(environment.getDbType()))
                && (environment.getExecutionEngine() != null && environment.getExecutionEngine() == 2))
            return DatabaseType.IMPALA;
        return dapSystemDatasource.getDatabaseType();
    }

    private String tryGetDbVersion(DapSystemDatasourceEnvironment environment, DatabaseType databaseType) {
        DatasourceQueryVo datasourceQueryVo = new DatasourceQueryVo();
        datasourceQueryVo.setAddress(environment.getHost());
        datasourceQueryVo.setPort(environment.getPort());
        datasourceQueryVo.setUsername(environment.getUsername());
        datasourceQueryVo.setPassword(environment.getPassword());
        datasourceQueryVo.setDatabaseName(environment.getDatabaseName());
        datasourceQueryVo.setDatabaseType(getDbProtocolTypeByEnv(databaseType, environment));
        datasourceQueryVo.setQuerySQL(getQueryVersionByDatabaseType(databaseType));
        datasourceQueryVo.setQuerySQLParams(null);
        datasourceQueryVo.setExtraProps(ImmutableMap.of(DatasourceQueryVo.QUERY_ONCE_ON_POOL, true));
        ApiTableResultBean<QueryResult> res = null;
        try {
            res = remoteApiTableService.datasourceCommonQuery(datasourceQueryVo);
            QueryResult content = res.getContent();
            if (content != null && content.querySelectData() != null) {
                return content.querySelectData().stream().findFirst()
                        .map(m -> m.values().stream().map(o -> Objects.toString(o, null)).collect(Collectors.joining(" ")))
                        .orElse("");
            }
        } catch (Exception e) {
            log.error("取数服务测试连接失败，{}", res, e);
        }
        if (DatabaseType.DORIS_DB.equals(databaseType)){
            databaseType = DatabaseType.MYSQL;
        }
        return ConnectUtils.connectTest(databaseType, environment);
    }

    private String getDbProtocolTypeByEnv(DatabaseType databaseType, DapSystemDatasourceEnvironment env) {
        switch (databaseType){
            case HIVE:
                if (Boolean.TRUE.equals(env.getIsInfoResource()))return env.getDbType() != null ? env.getDbType().name() : DatabaseType.MYSQL.name();
                return databaseType.getDbName();
            case DORIS_DB:
                return DatabaseType.MYSQL.getDbName();
            default:
                return databaseType.getDbName();
        }
    }

    private String getQueryVersionByDatabaseType(DatabaseType databaseType) {
        switch (databaseType) {
            case PHOENIX:
                return String.format("select '%s'", phoenixVersion);
            case HIVE:
            case DORIS_DB:
            case POSTGRESQL:
            case PGSQL:
            case MYSQL:
            default:
                return "select version()";
        }
    }

    // v1.3 数据源分页列表 ok
    @BmPermission(value = "system:database:list", name = "数据源列表")
    @GetMapping("datasource/list")
    @ApiOperation(value = "获取mysql和pg类型的数据源")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = DapSystemDatasource.class)})
    public ResultBean<List<DapSystemDatasource>> getDatasourceList(){

        List<Integer> databaseList = Arrays.asList(DatabaseType.MYSQL.getCode(), DatabaseType.PGSQL.getCode());
        LambdaQueryWrapper<DapSystemDatasource> in = new LambdaQueryWrapper<DapSystemDatasource>()
                .eq(DapSystemDatasource::getBmStatus, BmStatus.ON.getCode())
                .in(DapSystemDatasource::getDatabaseType, databaseList);
        List<DapSystemDatasource> datasourceList = dapSystemDatasourceService.getBaseMapper().selectList(in);
        List<String> collect = datasourceList.stream().map(DapSystemDatasource::getId).collect(Collectors.toList());
        List<DapSystemDatasourceEnvironment> environments = dapSystemDatasourceEnvironmentService.getBaseMapper()
                .selectList(new LambdaQueryWrapper<DapSystemDatasourceEnvironment>().
                eq(BaseModel::getBmStatus, BmStatus.ON.getCode()).eq(DapSystemDatasourceEnvironment::getIsInfoResource, false).
                in(DapSystemDatasourceEnvironment::getDatasourceId, collect));
        List<String> ids = environments.stream().map(DapSystemDatasourceEnvironment::getDatasourceId).collect(Collectors.toList());
        List<DapSystemDatasource> datasource = datasourceList.stream().filter(d -> ids.contains(d.getId())).collect(Collectors.toList());
        return ResultBean.ok(datasource);
    }

}
